package com.cseg674.dataentry.javaparser;
import java.io.*;
import com.hp.hpl.jena.rdf.model.*;
import java.util.ArrayList;

/**
 * Generates the final RDF output based on the tree
 */
public class TreeToRdf {

	/**
	 * Create the RDF file based on the Tree
	 * 
	 * @param tree		the tree that will be used to generate the RDF file
	 * @param ontology	the source ontology that will be used to extract RDF classes and properties from the tree
	 * @return 			returns the model of the RDF
	 */
	public Model ToRdf(RdfTree tree, SourceOntology ontology){

		int numClasses = tree.size();									// Number of Classes in the tree
		String outputFileName = DEApplication.getXMLName();				// Output File
		Resource[] classList = new Resource[numClasses];				// List of Classes
		ArrayList<Resource> methodList = new ArrayList<Resource>();		// List of Methods

		// Create an empty graph
		Model model = ModelFactory.createDefaultModel();

		// For each Class
		for(int i = 0 ; i < numClasses ; i++)
		{
			String Classname = tree.get(i).getName();					// Get the name of the Class from the tree
			classList[i] = model.createResource(ontology.oClass());
			classList[i].addProperty(ontology.oNamed(), Classname );	// Add the Class Name
			
			// Get all the fields (Member Variables) of the Class
			for(int j=0 ; j <tree.get(i).sizeOfFields() ; j++)
			{
				// Get Field Properties from the tree
				String FieldName = tree.get(i).getField(j).getName();		// Get Field Name from the tree
				String FieldType = tree.get(i).getField(j).getType();		// Get Field Type from the tree
				String FieldMod  = tree.get(i).getField(j).getModifier();	// Get Field Modifier from the tree
				
				// Add Field Properties to the Field Resource
				Resource FieldProperty = model.createResource(ontology.oField());
				FieldProperty.addProperty(ontology.oType(), FieldType);			// Add the Field Type
				FieldProperty.addProperty(ontology.oNamed(), FieldName);		// Add the Field Name
				if (FieldMod != null)
				{
					FieldProperty.addProperty(ontology.oModifier(), FieldMod);	// Add the Field Modifier
				}
				
				classList[i].addProperty(ontology.oHasField(), FieldProperty);	// Add the field to the class resource
			}
			
			// Get all the methods of the Class
			for(int j=0 ; j <tree.get(i).sizeOfMethods() ; j++)
			{
				// Get Method Properties
				String Methodname = tree.get(i).getMethod(j).getName();			// Get the Method Name from the tree
				String MethodReturn = tree.get(i).getMethod(j).getType();		// Get the Method Return Type from the tree
				String MethodMod = tree.get(i).getMethod(j).getModifier();		// Get the Method Modifier from the tree
				
				Resource methodProperty = model.createResource(ontology.oMethod());
				
				// Get all the parameters of the Method
				for(int k=0; k <tree.get(i).getMethod(j).sizeOfParams() ; k++)
				{
					// Get Parameter Properties From the tree
					String ParameterName = tree.get(i).getMethod(j).getParam(k).getName();	// Get the Parameter Name from the tree
					String ParameterType = tree.get(i).getMethod(j).getParam(k).getType();	// Get the Parameter Type from the tree
					
					// Add Parameter Properties to the Parameter Resource
					Resource parameterProperty = model.createResource(ontology.oParameter());
					parameterProperty.addProperty(ontology.oType(), ParameterType);		// Add Parameter Type
					parameterProperty.addProperty(ontology.oNamed(), ParameterName);	// Add Parameter Name
					
					// Add the parameter to the method Resource
					methodProperty.addProperty(ontology.oHasParameter(),parameterProperty);
				}
				
				// Add Method properties to the Method Resource
				methodProperty.addProperty(ontology.oNamed(), Methodname);		// Add Method Name
				methodProperty.addProperty(ontology.oType(), MethodReturn);		// Add Method Return Type
				methodProperty.addProperty(ontology.oModifier(), MethodMod);	// Add Method Modifier
				methodList.add(methodProperty);									
				
				// Add the method to the class Resource
				classList[i].addProperty(ontology.oHasMethod(), methodProperty);
			}
		}

		// Get the 'Calls' relationship between classes
		for(int i = 0 ; i < numClasses ; i++)
		{
			for(int j=0 ; j<tree.get(i).sizeOfMethods() ; j++)					// For each Method
			{			
				for(int k=0 ; k<tree.get(i).getMethod(j).sizeOfCalls(); k++)	// For each 'Calls'
				{
					// Get the method that calls
					RdfMethod tempMethod = tree.getMethod(tree.get(i),tree.get(i).getMethod(j).getCalls(k));
					
					if (tempMethod!=null) {
						Resource calling = null;
						Resource called = null;
						String Calls = tempMethod.getName();
						
						//find method calling
						for (int m=0; m<methodList.size(); m++) {
							if (tree.get(i).getMethod(j).getName().equals(methodList.get(m).getProperty(ontology.oNamed()).getString())) {
								calling = methodList.get(m);
							}
						}
						//find method called
						for (int m=0; m<methodList.size(); m++) {
							if (Calls.equals(methodList.get(m).getProperty(ontology.oNamed()).getString())) {
								called = methodList.get(m);
							}
						}
						
						if (calling != null && called != null) {
							calling.addProperty(ontology.oCalls(), called);
						}
					}
				}
			}
		}
		
		// Get the Inheritance and Interface relationships
		for(int i=0; i<numClasses; i++)
		{
			// Inheritance (extends) relationship
			if (tree.get(i).hasSuperClass())
			{
				Boolean derivedClassFound = false;
				Resource derivingClass = classList[i];
				String superClassName = tree.get(i).getSuperclass().getName();
				
				for (int j=0; j<numClasses; j++)
				{
					Resource derivedClass = classList[j];
					
					// Check if the super class name equals the name of a class in the tree
					if (superClassName.equals(tree.get(j).getName()))
					{
						// Set the class in a tree as a resource which has been derived from
						derivingClass.addProperty(ontology.oHasDerives(),derivedClass);
						derivedClassFound = true;
					}
				}
				
				// If the derived class was not found in the tree, it was not an input to JavaParser
				if (!derivedClassFound)
				{
					// Set the class name (not in the tree) as a property which has been derived from
					derivingClass.addProperty(ontology.oHasDerives(), tree.get(i).getSuperclass().getName());
				}
			}
			
			// Interface (implements) Relationship - Removed from implementation 2009, needs tweaking
			/*for (int k=0; k <tree.get(i).sizeOfInterfaces(); k++)	// Loop through each implemented class
			{
				Boolean implementedClassFound = false;
				Resource implementingClass = classList[i];
				String interfaceClassName = tree.get(i).getSuperInterface(k).getName();

				for (int j=0; j<tree.size(); j++)
				{
					Resource implementededClass = classList[j];
					
					// Check if the interface name equals the name of a class in the tree
					if (interfaceClassName.equals(tree.get(j).getName()))
					{
						// Set the class in a tree as a resource which has been implemented
						implementingClass.addProperty(ontology.oImplements(),implementededClass);
						implementedClassFound = true;
					}
				}
				
				// If the implemented class was not found in the tree, it was not an input to JavaParser
				if (!implementedClassFound)
				{
					// Set the class name (not in the tree) as a property which has been implemented from
					classList[i].addProperty(ontology.oImplements(), tree.get(i).getSuperInterface(k).getName());
				}
			}*/
		}
		
		// Create a file to output the RDF/XML and return the model.
		FileOutputStream rdfFile = null;
		
		try{
			rdfFile = new FileOutputStream(outputFileName);
		}
		catch(FileNotFoundException e)
		{
			System.err.println("Could not open file: " + outputFileName + ". Reason: " + e.getMessage() );
		}

		model.write(rdfFile, "RDF/XML");
		return model;
	}
}